package jmine.tec.proxy.chain;

import java.lang.reflect.Method;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Conta as invocacoes dos metodos. E thread-safe.
 * 
 * @author MarceloT
 * 
 */
public class MethodInvocationCount implements ChainElement {

    private ConcurrentMap<Method, AtomicLong> invocationCount =
            new ConcurrentHashMap<Method, AtomicLong>();

    /**
     * @param chain
     *            o invocationChain
     * @return Object the returned object
     * @throws Throwable
     *             e
     */
    public Object aroundInvoke(final InvocationChain chain) throws Throwable {
        Method method = chain.getInvokingMethod();
        AtomicLong counter = new AtomicLong(1);
        AtomicLong prev = invocationCount.putIfAbsent(method, counter);
        if (prev != null) {
            prev.getAndIncrement();
        }
        return chain.proceed();
    }

    /**
     * Imprime para a saida padrao
     */
    public void dumpInvocationCount() {
        String text = getInvocationCountDump();
        System.out.println(text);
    }

    /**
     * Gera uma string que contem todas as chamadas feitas até entao
     * 
     * @return String
     */
    public String getInvocationCountDump() {
        StringBuilder sb = new StringBuilder();
        Set<Entry<Method, AtomicLong>> entrySet = invocationCount.entrySet();
        for (Entry<Method, AtomicLong> entry : entrySet) {
            sb.append(entry.getKey());
            sb.append(" -> ");
            sb.append(entry.getValue());
            sb.append("\n");
        }
        String text = sb.toString();
        return text;
    }

}
